/*
 * Copyright (C) 2011-2013 Eric Lasota
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#ifndef __RDX_LONGFLOW_HPP__
#define __RDX_LONGFLOW_HPP__

#include "rdx_platform.hpp"

// Exception handling macros that can work with a C fallback when C++ exceptions are disabled
// For safety, code execution must always either rethrow or complete

#if defined(RDX_DISABLE_SANITY_CHECKS)

#define RDX_NTRY(lbl, ctx)					{

#define RDX_NPROTECT(lbl, ctx, invocation)	invocation

#define RDX_NPROTECT_ASSIGN(lbl, ctx, target, invocation)	(target) = (invocation)

#define RDX_NCATCH(lbl, ctx)				} if(false) {

#define RDX_NENDTRY(lbl)					}

#define RDX_RETHROW(ctx)					do {} while(false)

#define RDX_RETHROWV(ctx, v)				do {} while(false)


#define RDX_LTHROW(ctx, errCode)		do {} while(0)

#define RDX_LTHROWV(ctx, errCode, v)	RDX_LTHROW(ctx, errCode)

#define RDX_NSTHROW(lbl, ctx, errCode)	RDX_LTHROW(ctx, errCode)

#elif defined(RDX_USE_CPP_EXCEPTIONS)

namespace RDX
{
	struct OperationContext;

	class InternalException
	{
		OperationContext *_ctx;
		int _errorCode;
	public:
		inline InternalException(OperationContext *ctx, int errorCode)
		{
			_ctx = ctx;
			_errorCode = errorCode;
		}

		inline int ErrorCode() const
		{
			return _errorCode;
		}

		inline OperationContext *OperationContext() const
		{
			return _ctx;
		}
	}
}

#define RDX_NTRY(lbl, ctx)					try {

#define RDX_NPROTECT(lbl, ctx, invocation)	invocation

#define RDX_NPROTECT_ASSIGN(lbl, ctx, target, invocation)	(target) = (invocation)

#define RDX_NCATCH(lbl, ctx)				} catch(const RDX::InternalException &__tdp_iex) { int errorCode = __tdp_iex->ErrorCode();

#define RDX_NENDTRY(lbl)					}

#define RDX_RETHROW(ctx)					throw

#define RDX_RETHROWV(ctx, v)				throw


#define RDX_LTHROW(ctx, errCode)		do\
										{\
											throw RDX::InternalException ((ctx), (errCode));\
										} while(0)

#define RDX_LTHROWV(ctx, errCode, v)	RDX_LTHROW(ctx, errCode)

#define RDX_NSTHROW(lbl, ctx, errCode)	RDX_LTHROW(ctx, errCode)

#else

#define RDX_NTRY(lbl, ctx)					{\
												if((ctx)->errorCode)\
													goto errorHandler_##lbl;\
												(ctx)->errorCode = 0;

#define RDX_NPROTECT(lbl, ctx, invocation)	do {\
												invocation;\
												if((ctx)->errorCode)\
													goto errorHandler_##lbl;\
											} while(0)


#define RDX_NPROTECT_ASSIGN(lbl, ctx, target, invocation)	do {\
																target = invocation;\
																if((ctx)->errorCode)\
																	goto errorHandler_##lbl;\
															} while(0)

#define RDX_NCATCH(lbl, ctx)					goto noError_##lbl;\
												errorHandler_##lbl:\
												{\
													int errorCode = (ctx)->errorCode;\
													(ctx)->errorCode = 0;

#define RDX_NENDTRY(lbl)						}\
												noError_##lbl:;\
											}

#define RDX_RETHROW(ctx)			(ctx)->errorCode = errorCode;\
									RDX::Utility::DebugBreak(RDX::Utility::BREAKCAUSE_Exception);\
									return

#define RDX_RETHROWV(ctx, v)		(ctx)->errorCode = errorCode;\
									RDX::Utility::DebugBreak(RDX::Utility::BREAKCAUSE_Exception);\
									return v


#define RDX_NSTHROW(lbl, ctx, errCode)	do\
										{\
											(ctx)->errorCode = errCode;\
											RDX::Utility::DebugBreak(RDX::Utility::BREAKCAUSE_Exception);\
											goto errorHandler_##lbl;\
										} while(0)


#define RDX_LTHROW(ctx, errCode)	do\
									{\
										(ctx)->errorCode = errCode;\
										RDX::Utility::DebugBreak(RDX::Utility::BREAKCAUSE_Exception);\
										return;\
									} while(0)

#define RDX_LTHROWV(ctx, errCode, v)	do\
									{\
										(ctx)->errorCode = errCode;\
										RDX::Utility::DebugBreak(RDX::Utility::BREAKCAUSE_Exception);\
										return v;\
									} while(0)


#endif


#define RDX_TRY(ctx)								RDX_NTRY(EH, ctx)
#define RDX_PROTECT(ctx, invocation)				RDX_NPROTECT(EH, ctx, invocation)
#define RDX_PROTECT_ASSIGN(ctx, target, invocation)	RDX_NPROTECT_ASSIGN(EH, ctx, target, invocation)
#define RDX_CATCH(ctx)								RDX_NCATCH(EH, ctx)
#define RDX_ENDTRY									RDX_NENDTRY(EH)
#define RDX_STHROW(ctx, errCode)					RDX_NSTHROW(EH, ctx, errCode)


// C++ style:
//struct RDX_EXCEPTION_CONTEXT		{ int errorCode; }
//#define RDX_TRY(ctx)				try
//#define RDX_CATCH(ctx)			catch(RDX::OperationContext *c) { int errorCode = c->errorCode;
//#define RDX_ENDTRY(ctx)			}
//#define RDX_THROW(ctx, errCode)	do { (ctx)->errorCode = errCode; throw (ctx); } while(0)



namespace RDX
{
	namespace Programmability
	{
		struct Exception;
	}

	namespace ObjectManagement
	{
		struct IObjectManager;
	}

	namespace Errors
	{
		enum
		{
			RDX_ERROR_NONE					= 0,

			RDX_ERROR_ALLOCATION_FAILED		= 1000,
			RDX_ERROR_UNRESOLVED_SYMBOL_REFERENCE,
			RDX_ERROR_PACKAGE_CORRUPTED,
			RDX_ERROR_INCOMPATIBLE_CONVERSION,
			RDX_ERROR_DUPLICATED_SYMBOL_MISMATCH,
			RDX_ERROR_FORBIDDEN_SYMBOL_DUPLICATION,
			RDX_ERROR_NON_DUPLICABLE_SYMBOL_COLLISION,
			RDX_ERROR_MUSTBECONST_PROPERTY_REFERENCES_NON_CONST_ARRAY,
			RDX_ERROR_FORBIDDEN_CLASS_EXTENSION,
			RDX_ERROR_DOMAIN_POLICY_VIOLATION,
			RDX_ERROR_FORBIDDEN_VARYING_USAGE,

			RDX_ERROR_INCOMPATIBLE_VTABLE,
			RDX_ERROR_INCOMPATIBLE_PROPERTIES,
			RDX_ERROR_NATIVE_METHOD_UNBOUND,
			RDX_ERROR_CREATED_ABSTRACT_TYPE,
			RDX_ERROR_INTEGER_OVERFLOW,
			RDX_ERROR_RESUMED_UNRECOVERED_THREAD,

			RDX_ERROR_INTERNAL_DUPLICATE_LOST_GST,
			RDX_ERROR_INTERNAL_BAD_TYPE,
			RDX_ERROR_INTERNAL_DESYNCED_COUNTER,
			RDX_ERROR_INTERNAL_GENERAL,

			RDX_ERROR_SAVED_PACKAGED_OBJECT,

			RDX_ERROR_RECURSION_LIMIT_EXCEEDED,
			RDX_ERROR_PRECALL_STACK_OVERFLOW,

			RDX_ERROR_INVALID_PATCH,

			RDX_ERROR_DESERIALIZED_THREAD_WITHOUT_FULL_JOURNALING,

			RDX_ERROR_INVALID_PARAMETER,

			RDX_ERROR_IL_STACK_UNDERFLOW,
			RDX_ERROR_IL_FORBIDDEN_OPERATION_STATE,
			RDX_ERROR_IL_UNALIGNABLE_BYVAL,
			RDX_ERROR_IL_INVALID_OPERAND,
			RDX_ERROR_IL_STACKS_UNBALANCED,
			RDX_ERROR_IL_INVALID_PROPERTY,
			RDX_ERROR_IL_INVALID_JUMP_TARGET,
			RDX_ERROR_IL_ARGUMENT_UNDERRUN,
			RDX_ERROR_IL_INVALID_OPCODE,
		};
	};

	namespace Threading
	{
		struct IMutex;
	};

	enum
	{
		COREMUTEX_OperationCheck,
		COREMUTEX_GlobalOperation,				// Blocks global operations and thread initiation
		COREMUTEX_SymbolTableAccess,			// Blocks symbol table access and package operations
		COREMUTEX_AllocateObject,				// Blocks object creation
		COREMUTEX_StringTableInsert,			// Blocks string table insertions
		
		COREMUTEX_PersistentObjectList,			// Won't cause a global block

		COREMUTEX_Count,
	};

	struct OperationContext
	{
		Programmability::Exception *ex;
		ObjectManagement::IObjectManager *objm;
		int errorCode;

		int mutexAccess[COREMUTEX_Count];

		explicit OperationContext(ObjectManagement::IObjectManager *objm);
		virtual ~OperationContext();
		
		bool TryAcquire(ObjectManagement::IObjectManager *objm, int mNum, int msec = 0);
		void Acquire(ObjectManagement::IObjectManager *objm, int mNum, int msec = 0);
		void Release(ObjectManagement::IObjectManager *objm, int mNum);

		void Deaden();
	};

};

#include "rdx_threading.hpp"
#include "rdx_objectmanagement.hpp"

namespace RDX
{
	inline bool OperationContext::TryAcquire(ObjectManagement::IObjectManager *objm, int mNum, int msec)
	{
		if(mutexAccess[mNum] == 0 &&
			!objm->GetCoreMutex(mNum)->TryAcquire(msec))
				return false;

		mutexAccess[mNum]++;
		return true;
	}

	inline void OperationContext::Acquire(ObjectManagement::IObjectManager *objm, int mNum, int msec)
	{
		if(!mutexAccess[mNum])
			objm->GetCoreMutex(mNum)->Acquire(msec);
		mutexAccess[mNum]++;
	}

	inline void OperationContext::Release(ObjectManagement::IObjectManager *objm, int mNum)
	{
		mutexAccess[mNum]--;
		if(!mutexAccess[mNum])
			objm->GetCoreMutex(mNum)->Release();
	}

	inline OperationContext::OperationContext(ObjectManagement::IObjectManager *objm)
	{
		errorCode = 0;
		for(int i=0;i<COREMUTEX_Count;i++)
			mutexAccess[i] = 0;

		this->objm = objm;
		if(objm)
			objm->IncrementThreadCounter();
	}

	inline OperationContext::~OperationContext()
	{
		Deaden();
	}

	inline void OperationContext::Deaden()
	{
		if(objm)
			objm->DecrementThreadCounter();
		objm = NULL;
	}
}

#endif
